/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package schtohan.business;

import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ApplicationScoped;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import schtohan.entities.Journey;
import schtohan.entities.Stop;
import schtohan.restclient.VasttrafikClient;


/**
 *
 * @author Schtohan
 */
@ManagedBean(eager=true)
@ApplicationScoped

public class ScheduleBean {
    
    private ScheduledExecutorService scheduler;  
    
    public ScheduleBean() {

    }
    
    @PostConstruct
    public void init() {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        delayChecker delayCheck = new delayChecker();
        //Remove comment to run the delay stat. application
        //scheduler.scheduleAtFixedRate(delayCheck, 0, 1, TimeUnit.MINUTES);
    }

    @PreDestroy
    public void destroy() {
        scheduler.shutdownNow();
    }
}
  class delayChecker implements Runnable {
      
    private ArrayList<Journey> journeys;  
    private ArrayList<Journey> currentJourneys;
    private ArrayList<Journey> finishedJourneys;
    private ArrayList<Journey> journeysToRemove;
    private Journey nextJourney;    
    private String nextJourneyStartTime;
    private VasttrafikClient client;
    private DbController dbController;
    private final String LINJE = "Spårvagn 1";
    
    private String stopName = "Opaltorget";
      
    public delayChecker() {
        client = new VasttrafikClient();
        journeys = new ArrayList<Journey>();
        currentJourneys = new ArrayList<Journey>();
        finishedJourneys = new ArrayList<Journey>();
        journeysToRemove = new ArrayList<Journey>();
        dbController = new DbController();     
        fetchJourneys();   
    }
    
    @Override
    //Executed once every minute
    public void run()
    {
        dbController.createJourneyStops(new Journey(), new ArrayList<Stop>());

        //If next journey's first departure time is now, add that journey to current           
        String now = DateFormat.getTimeInstance(DateFormat.SHORT).format(new Date());   
        
        if(nextJourneyStartTime.equals(now))
        {
            currentJourneys.add(nextJourney);
            fetchJourneyDetails(nextJourney);
            nextJourney = journeys.get(journeys.indexOf(nextJourney) + 1);   
            nextJourneyStartTime = nextJourney.getStops().get(0).getDepTime();
        }
        
        //For each currentJourney, if the tram has departed from it's current stop, check difference between scheduled and real departure time, 
        //and save the difference. 
        for(Journey j : currentJourneys)        
        {                 
            if(isDeparted(j))
            {
                String schedTime = j.getNextStop().getDepTime(); 
                saveTimeDifference(schedTime, now, j); 
                //if arrived at last stop
                if(j.getNextStop().getStopNr() == j.getLastStopNr())
                {
                    finishedJourneys.add(j);
                    //save to database
                    dbController.createJourneyStops(j, j.getStops());
                    journeysToRemove.add(j);
                }
                else
                {
                    j.incrementNextStop();
                }
            }
        }
        
        if(journeysToRemove.size() != 0)
        {
            for(Journey j : journeysToRemove) {
                currentJourneys.remove(j);
            }
            
            journeysToRemove.clear();
        }
        
        
    }
        
    //Gets an ArrayList of all journeys for the next 24 hours, for a particular line in a particular direction
    public void fetchJourneys()
    {
        try
        {
            String stopId = getStopId(stopName);
            String depBoard = client.departureBoard(stopId, "1439");
            ObjectMapper mapper = new ObjectMapper();
            JsonNode root = mapper.readTree(depBoard);
            Iterator<JsonNode> depIterator = root.path("DepartureBoard").path("Departure").getElements();
            while(depIterator.hasNext())
            {
                JsonNode thisDep = depIterator.next();
                String linje = thisDep.path("name").getTextValue();
                if(linje.equals(LINJE))
                {
                    Stop stop = new Stop(thisDep.path("stop").getTextValue(), thisDep.path("time").getTextValue());
                    journeys.add(new Journey(thisDep.path("JourneyDetailRef").path("ref").getTextValue(), stop));
                }     
            }
            
            nextJourney = journeys.get(0);
            nextJourneyStartTime = nextJourney.getStops().get(0).getDepTime();
            
        }
        
        catch(Exception e) {}
    }
    
    private String getStopId(String stop)
    {
        try
        {
            //Hämta resultat för sökning på hållplats
            String locations = client.Location(stop);
            ObjectMapper mapper = new ObjectMapper();   
            JsonNode root = mapper.readTree(locations);
            JsonNode stops = root.path("LocationList").path("StopLocation");
            
            return stops.get(0).path("id").getTextValue();
        }
            
        catch(Exception e) {
            return null;
        }

    }

    private void fetchJourneyDetails(Journey nxtJourney) {
        
        try
        {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode root = mapper.readTree(new URL(nxtJourney.getAdress()));
            Iterator<JsonNode> depIterator = root.path("JourneyDetail").path("Stop").getElements();
            
            JsonNode firstStop = depIterator.next();           
            String startDate = firstStop.path("depDate").getTextValue();
            String startTime = firstStop.path("depTime").getTextValue();
            SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
            SimpleDateFormat sdfTime = new SimpleDateFormat("HH:mm");
            nxtJourney.setStartDate(sdfDate.parse(startDate));
            nxtJourney.setStartTime(sdfTime.parse(startTime));
            
            nxtJourney.getStops().get(0).setStopNr(Integer.parseInt(firstStop.path("routeIdx").getTextValue())); 
            while(depIterator.hasNext())
            {
                JsonNode thisStop = depIterator.next();
                Stop stop = new Stop(thisStop.path("name").getTextValue(), thisStop.path("depTime").getTextValue(), Integer.parseInt(thisStop.path("routeIdx").getTextValue()), nxtJourney);
                nxtJourney.getStops().add(stop);                    
            }
            
            List<Stop> stops = nxtJourney.getStops();
            Stop last = stops.get(stops.size() - 1);
            nxtJourney.setLastStopNr(last.getStopNr());
            nxtJourney.setDirection(last.getName());
            nxtJourney.setNextStop(nxtJourney.getStops().get(0));
            nxtJourney.setLine(LINJE);
        }
        
        catch (Exception e){}
    }
    
    //Kollar om spårvagnen avgått från aktuell hållplats
    private boolean isDeparted(Journey currentJourney)
    {
        try
        {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode root = mapper.readTree(new URL(currentJourney.getAdress()));
            int currentStopNr = currentJourney.getNextStop().getStopNr();
            //Den nyss hämtade infon för den aktuella hållplatsen
            JsonNode currentStopUpdated = root.path("JourneyDetail").path("Stop").get(currentStopNr);
            //rtArrTime försvinner när spårvagn avgått, vilket innebär att rtArrTime == null betyder att spårvagnen avgått från aktuell hållplats
            if(currentStopUpdated.path("routeIdx").getTextValue().equals("0"))
            {
                if(currentStopUpdated.has("rtDepTime")) {
                    return false;
                }
                else {
                    return true;
                }
            }
            else {
                if(currentStopUpdated.has("rtArrTime")) {
                    return false;
                }
                else {
                    return true;
                }
            }
        }
        catch(Exception e){
        }
        
        return false;
    }

    private void saveTimeDifference(String schedTime, String rlTime, Journey j) {
         
        try
        {
            DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT); 
            Date scheduledTime = df.parse(schedTime);
            Date realTime = df.parse(rlTime);
            long diff = realTime.getTime() - scheduledTime.getTime();
            String diffInMinutes = Long.toString(diff / 60000);   
            j.getNextStop().setDelay(diffInMinutes);
        }
        catch (Exception e) {}
    }

}